package com.xunjing.order.config.sharding;

import com.google.common.collect.Range;
import com.xunjing.common.core.utils.DateUtils;
import com.xunjing.common.core.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.sql.*;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author ZYT
 * @Date 2024-04-01 14:33
 * @Version 1.0
 * @Describe 范围分片(如果没指定分片字段 ， 则不走)
 */
@Slf4j
@Component
public class FlagRangeShardingAlgorithm implements RangeShardingAlgorithm<String> {
    private static final String dataSourceName = "";

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
        String logicTableName = rangeShardingValue.getLogicTableName();
        // 打印分片信息
        log.info(">>>>>>>>>> 【INFO】范围分片，节点配置表名：{}", rangeShardingValue);
        Range<String> valueRange = rangeShardingValue.getValueRange();
        boolean hasLowerBound = valueRange.hasLowerBound();
        boolean hasUpperBound = valueRange.hasUpperBound();

        int beginTable = hasLowerBound ? Integer.parseInt(valueRange.lowerEndpoint()) : 0;
        int endTable = hasUpperBound ? Integer.parseInt(valueRange.upperEndpoint()) : 0;
        // 定义匹配模式
        String pattern = "^(\\d{4})(0[1-9]|1[0-2])$";
        // 编译正则表达式
        Pattern r = Pattern.compile(pattern);

        // 判断这两个日期所跨越表的范围
        if (beginTable == 0 || endTable == 0 || !r.matcher(beginTable+"").find() || !r.matcher(endTable+"").find()) return (Collection<String>) new IllegalArgumentException("创建异常");

        // 判断这两个年月后缀所跨越表的范围
        Set<String> resultTableNames = new HashSet<>();
        // 循环处理每个年月
        int currentYearMonth = beginTable;
        while (currentYearMonth <= endTable) {
            // 处理当前年月
            int year = currentYearMonth / 100;
            int month = currentYearMonth % 100;
            String formattedMonth = String.format("%02d", month);
            resultTableNames.add(logicTableName + "_" + year+formattedMonth);
            // 递增当前年月
            if (month == 12) {
                currentYearMonth = (year + 1) * 100 + 1;
            } else {
                currentYearMonth++;
            }
        }
        log.info("范围分跨越分表：{}", resultTableNames);
        return getShardingTablesAndCreate(logicTableName, resultTableNames, collection);
    }

    /**
     * 检查分表获取的表名是否存在
     *
     * @param logicTableName       逻辑表
     * @param resultTableNames     真实表名，例：t_user_202201
     * @param availableTargetNames 可用的数据库表名
     * @return 存在于数据库中的真实表名集合
     */
    public Set<String> getShardingTablesAndCreate(String logicTableName, Collection<String> resultTableNames, Collection<String> availableTargetNames) {
        return resultTableNames.stream().map(o -> getShardingTableAndCreate(logicTableName, o, availableTargetNames)).collect(Collectors.toSet());
    }

    /**
     * 检查分表获取的表名是否存在
     *
     * @param logicTableName  逻辑表
     * @param resultTableName 真实表名，例：t_user_202201
     * @return 确认存在于数据库中的真实表名
     */
    private String getShardingTableAndCreate(String logicTableName, String resultTableName, Collection<String> availableTargetNames) {
        // 缓存中有此表则返回，没有则判断创建
        if (availableTargetNames.contains(resultTableName)) {
            return resultTableName;
        } else {
            // 检查分表获取的表名不存在，存在则加入缓存
            boolean isSuccess = existShardingTable(logicTableName, resultTableName);
            if (isSuccess) {
                //添加缓存
                availableTargetNames.add(resultTableName);
                return resultTableName;
            } else {
                // 如果建表失败，返回逻辑空表
                return logicTableName;
            }
        }
    }

    //判断表是否存在
    private boolean existShardingTable(String logicTableName, String resultTableName) {
        // 根据日期判断，当前月份之后分表不提前创建
        String month = resultTableName.replace(logicTableName + "_", "");
        YearMonth shardingMonth = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyyMM"));
        if (shardingMonth.isAfter(YearMonth.now())) {
            return false;
        }
        //创建锁
        synchronized (logicTableName.intern()) {
            Environment env = SpringUtil.getApplicationContext().getEnvironment();
            //构造sql
            String sql = "SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = '" + env.getProperty("my-conf.data-source-name") + "' AND table_name = '" + resultTableName + "'";
            try (Connection conn = DriverManager.getConnection(env.getProperty("spring.shardingsphere.datasource.master.url"), env.getProperty("spring.shardingsphere.datasource.master.username"), env.getProperty("spring.shardingsphere.datasource.master.password"))) {
                try (Statement st = conn.createStatement()) {
                    conn.setAutoCommit(false);
                    ResultSet resultSet = st.executeQuery(sql);
                    // 读取结果集中的计数值
                    resultSet.next();
                    int count = resultSet.getInt("count");
                    // 判断计数值是否为1
                    if (count == 1) {
                        System.out.println("数据库已存在");
                        return true;
                    } else {
                        System.out.println("数据库不存存在");
                    }
                } catch (Exception e) {
                    conn.rollback();
                    log.error(">>>>>>>>>> 【ERROR】数据表查询执行失败，请稍后重试，原因：{}", e.getMessage(), e);
                    throw new IllegalArgumentException("数据表查询执行失败，请稍后重试");
                }
            } catch (SQLException e) {
                log.error(">>>>>>>>>> 【ERROR】数据库连接失败，请稍后重试，原因：{}", e.getMessage(), e);
                throw new IllegalArgumentException("数据库连接失败，请稍后重试");
            }
        }
        return false;
    }

}
